#include "stdafx.h"
#pragma hdrstop

#include "OSThread.h"
#include "P3Exception.h"
#include "ClassName.h"

#include <process.h>
#include <cstdlib>
#include <cstring>
#include <cerrno>

using namespace std;


OSThread::OSThread()
	: mThreadId(0),
	  mResult(0),
	  mTerminating(false),
	  mTerminated(true),
	  mpTerminationEvent(new OSEvent)
{
	mpTerminationEvent->Set();
}

void OSThread::Start()
{
	TerminateWait();
	OSMutex::Lock lock(mMutex);
	mTerminating = false;
	uintptr_t handle = ::_beginthreadex(NULL, 0, OSThread::StartThread, this, 0, &mThreadId);
	if(handle)
	{
		::CloseHandle((HANDLE)handle);
		mTerminated = false;
		mpTerminationEvent->Reset();
	}else
		throw p3exception(::strerror(errno));
}

bool OSThread::InOwnThread() const
{
	return ::GetCurrentThreadId() == mThreadId;
}

OSThread::~OSThread()
{
	if(!IsTerminated())
	{
		throw p3exception("Thread still running when being destructed -- "
						  "call OSThread::TerminateWait() from your derived class' destructor for a fix");
	}
}

SharedPointer<OSEvent> OSThread::Terminate()
{
	OSMutex::Lock lock(mMutex);
	mTerminating = true;
	return mpTerminationEvent;
}

bool OSThread::TerminateWait(int timeout_ms /* = OSEvent::cInfiniteTimeout */)
{
	if(InOwnThread())
		throw p3exception("Call from own thread, throwing exception to prevent dedlock");
	
	return Terminate()->Wait(timeout_ms);
}

int OSThread::CallExecute()
{
	// we can put some code to catch error in this function
	return OnExecute();
}

void OSThread::CallFinished()
{
	// we can put some code to catch error in this function
	return OnFinished();
}

unsigned int _stdcall OSThread::StartThread(void* Instance)
{
	OSThread* this_ = static_cast<OSThread*>(Instance);
	int result = this_->CallExecute();
	this_->FinishThread(result);
	return result;
}

void OSThread::FinishThread(int inResult)
{
	// the OnFinished handler may delete the OSThread Object, so we
	// use a temporary shared pointer to the termination event.
	SharedPointer<OSEvent> pTerminationEvent;
	{
		OSMutex::Lock lock(mMutex);
		mResult = inResult;
		mTerminated = true;
		pTerminationEvent = mpTerminationEvent;
	}
	CallFinished();
	pTerminationEvent->Set();
}
